package org.durandj.ystream.client.network;

import java.security.InvalidParameterException;
import java.util.LinkedList;

import org.durandj.ystream.client.network.packetHandlers.DefaultPacketHandler;
import org.durandj.ystream.client.util.Timer;

/**
 * Handles the sorting of received packets and enforces
 * that the packet handlers are properly handling the packets
 * they receive.
 * 
 * @author durandj
 * @version 1.0
 *
 */
public class PacketManager
{
	/**
	 * The packet handlers.
	 */
	private LinkedList<PacketHandler> handlers;
	
	/**
	 * The flag that keeps the process running.
	 */
	private boolean alive;
	
	/**
	 * The thread of execution.
	 */
	private Thread thread;
	
	/**
	 * Creates a packet handler manager. The parameter defines how many opcodes
	 * are expected. These opcodes are also assumed to be sequential in nature.
	 * 
	 * @param nOpcodes - the number of opcodes.
	 */
	public PacketManager(int nOpcodes)
	{
		if(nOpcodes < 0)
			throw new InvalidParameterException("Invalid number of opcodes.");
		
		handlers = new LinkedList<PacketHandler>();
		for(int i = 0; i != nOpcodes; i++)
			handlers.add(new DefaultPacketHandler());
		
		alive = true;
		
		thread = new Thread(new Runnable() {
			@Override
			public void run()
			{
				for(int current = 0; alive; current = (current == handlers.size() - 1) ? 0:(++current))
				{
					new Timer(50);
					
					PacketHandler handler = handlers.get(current);
					
					if(handler.noPackets())
						continue;
					
					handler.handlePacket(handler.nextPacket());
				}
				
				handlers.clear();
			}
		});
		thread.start();
	}
	
	/**
	 * Sets a packet handler to a new packet handler. This is used
	 * to change the packet handler type to something different.
	 * 
	 * @param opcode - the opcode of the packer handler
	 * @param handler - the packet handler reference
	 */
	public void setPacketHandler(int opcode, PacketHandler handler)
	{
		if(opcode < 0 || opcode >= handlers.size())
			throw new InvalidParameterException("Invalid opcode.");
		
		if(handler == null)
			throw new NullPointerException("Invalid handler reference.");
		
		handlers.set(opcode, handler);
	}
	
	/**
	 * Stops packet handling.
	 */
	public void stopHandlers()
	{
		alive = false;
	}
	
	/**
	 * Sends a packet to the packet handlers.
	 * 
	 * @param packet - a network packet
	 */
	public void handlePacket(Packet packet)
	{
		if(packet == null)
			throw new NullPointerException("Invalid packet.");
		
		handlers.get(packet.getOpcode()).queuePacket(packet);
	}
	
	/**
	 * Gets the number of opcodes that are managed.
	 * 
	 * @return number of managed opcodes
	 */
	public int getOpcodeNumber()
	{
		return handlers.size();
	}
}